/* ==============================================================================
*
*   Copyright (c) 2010, Pierre Favier
*   All rights reserved.
*
*   NTasking is free software: you can redistribute it and/or modify it
*   under the terms of the GNU General Public License as published by the
*   Free Software Foundation.
*
*   Redistribution and use in source and binary forms, with or without
*   modification, are permitted provided that the following conditions
*   are met:
*
*   -   Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*
*   -   Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials
*       provided with the distribution.
*
*   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
*   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
*   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
*   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
*   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
*   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
*   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
*   THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*   SUCH DAMAGE.
*
* ============================================================================== */

using System;
using NUnit.Framework;
using Rhino.Mocks;

namespace NTasking.ValidationTests.ACATS_3_0.C9
{

    [TestFixture(Description = "Check that aborts are deferred during protected actions.")]
    public class C980002 : ValidationTest
    {

        const int MaxCheckpoints = 7;

        private static Checkpoint checkpoint;
        private static StartHere startHere;
        private static MiddlePO middlePO;
        private static FinalPO finalPO;


        private static bool[] CreateCheckpoint()
        {
            return new bool[MaxCheckpoints + 1];
        }


        internal class Checkpoint : Protected
        {

            [ReadWriteOperation]
            internal Action<int> GotHere { get; private set; }

            [ReadOnlyOperation]
            internal Func<bool[]> Results { get; private set; }


            private bool[] ReachedPoints = CreateCheckpoint();


            internal Checkpoint()
            {
                ReadWriteOperation(GotHere, (i) =>
                {
                    ReachedPoints[i] = true;
                });


                ReadOnlyOperation(Results, () =>
                {
                    return ReachedPoints;
                });
            }
        }


        internal class StartHere : Protected
        {
            [Entry]
            internal Action ASTWaitHere { get; private set; }

            [Entry]
            internal Action StartPO { get; private set; }

            [Entry]
            private Action FirstStop { get; set; }

            private bool Open = false;

            internal StartHere()
            {
                Entry(ASTWaitHere,
                    When: () => Open,
                Is: () =>
                {
                    // null
                });

                Entry(StartPO,
                    When: () => true,
                Is: () =>
                {
                    Open = true;
                    checkpoint.GotHere(1);
                    Requeue(FirstStop);
                });

                Entry(FirstStop,
                When: () => Count(ASTWaitHere) == 0,
                Is: () =>
                {
                    checkpoint.GotHere(2);
                    Requeue(middlePO.Stop1);
                });
            }
        }


        internal class MiddlePO : Protected
        {
            [Entry]
            internal Action Stop1 { get; private set; }

            [Entry]
            internal Action Stop2 { get; private set; }

            internal MiddlePO()
            {
                Entry(Stop1,
                    When: () => true,
                Is: () =>
                {
                    checkpoint.GotHere(3);
                    Requeue(Stop2);
                });

                Entry(Stop2,
                    When: () => true,
                Is: () =>
                {
                    checkpoint.GotHere(4);
                    Requeue(finalPO.FinalStop);
                });

            }
        }


        internal class FinalPO : Protected
        {
            [Entry]
            internal Action FinalStop { get; private set; }

            internal FinalPO()
            {
                Entry(FinalStop,
                    When: () => true,
                Is: () =>
                {
                    checkpoint.GotHere(5);
                });
            }
        }



        [Test(Description =
            "This test uses an asynchronous transfer of control to attempt" +
            "to abort a protected operation.  The protected operation" +
            "includes several requeues to check that the requeue does not" +
            "allow the abort to occur.")]
        public void Test()
        {
            IValidationTestResult result = Mocks.DynamicMock<IValidationTestResult>();

            Ada.Declare(() =>
            {
                checkpoint = new Checkpoint();
                startHere = new StartHere();
                middlePO = new MiddlePO();
                finalPO = new FinalPO();

                // Setup text expectations

                Expect.Call(result.TestFailed).Repeat.Never();

                Mocks.ReplayAll();
            },
            Begin: () =>
            {
                // Start test execution

                Ada.Select(s => s.
                    Call(startHere.ASTWaitHere)().
                    Then(() =>
                    {
                        checkpoint.GotHere(6);
                    }).
                ThenAbort(() =>
                {
                    startHere.StartPO();
                    Ada.Delay(0.0); // Abort completion point
                    checkpoint.GotHere(7);
                }));

                bool[] chk = checkpoint.Results();

                for (int i = 1; i < 7; i++)
                {
                    if (!chk[i])
                        result.TestFailed();
                }

                if (chk[7])
                    result.TestFailed();
            },
            Exception: (ex) =>
            {
                result.TestFailed();
                return false;
            });

        }

    }

}
